/**
 * 
 */
package hu.myai.calculators;

import hu.myai.model.db.AxHistory;
import hu.myai.model.graph.AbstractNode;
import hu.myai.model.graph.FibNode;
import hu.myai.model.misc.KeyValue;
import hu.myai.model.sp.StockPivot;
import hu.myai.util.Constans;
import hu.myai.util.DatabaseUtil;

import java.util.Date;
import java.util.List;

/**
 * @author bozbalint
 * 
 */
abstract public class AbstractFibonacciFan extends AbstractCalculator {

	protected AbstractFibonacciFan(String symbol, StockPivot maxStock, StockPivot minStock, List<AxHistory> stockList,
			List<StockPivot> stockPivotList) {
		super(symbol, maxStock, minStock, stockList, stockPivotList);
	}

	// for database util reference
	private DatabaseUtil dbUtil = new DatabaseUtil();

	abstract protected FibNode getFibFanNode(StockPivot stockPivot);

	abstract protected double getPercentFromFanSupport(StockPivot stockPivot);

	abstract protected double getTangensAlpha(StockPivot stockPivot);

	abstract protected void setFibFanNode(StockPivot stockPivot, FibNode fibFanNode);

	abstract protected void setPercentFromFanSupport(StockPivot stockPivot, double percentFromFanSupport);

	abstract protected void setTangensAlpha(StockPivot stockPivot, double tangensAlpha);

	/**
	 * @param baseStockPivot
	 *            the fibonacci 0 level, this is the base or the levels
	 *            calculation
	 * @param distanceMinMax
	 *            the difference between season low and season high
	 * @return number of touch
	 */
	protected int setFibFanNodeValues(StockPivot baseStockPivot, StockPivot nullPivot) {

		// init fig graph
		FibNode actFibFanNode = (FibNode) Constans.bf_fib.getBean("fib_000");

		// remove the set values from the stock Pivot list
		List<StockPivot> aftreNullList = stockPivotList.subList(stockPivotList.indexOf(nullPivot), stockPivotList
				.size());

		int numberOfTouch = 0;

		// calculate a fib fan parameter that is the best fit
		double tgZeroCalculated = getBestFibDistance(baseStockPivot, nullPivot);

		for (StockPivot stockPivot : aftreNullList) {

			// distance from the base
			double yAct = Math.abs(stockPivot.getValue() - baseStockPivot.getValue());
			long xAct = dbUtil.recordsInBetween(symbol, baseStockPivot.getLocation(), stockPivot.getLocation());

			// this is the ratio between the elapsed time and the difference of
			// sock values
			double actTangensAlpha = yAct / xAct;

			// make a map between fibonacci level and fibonacci fan calculation.
			double actLevel = actTangensAlpha / tgZeroCalculated;

			FibNode fibFanNode = actFibFanNode.getNextNode(actLevel);
			if (fibFanNode != null) {
				actFibFanNode = fibFanNode;
				numberOfTouch++;
			}

			setFibFanNode(stockPivot, fibFanNode);
			setTangensAlpha(stockPivot, actTangensAlpha);
			setPercentFromFanSupport(stockPivot, actLevel);
		}
		return numberOfTouch;
	}

	/**
	 * @param baseStockPivot
	 *            the start point of the fibonaccifan
	 * @param nullPivot
	 *            the 0 level of the fibonacci fan
	 * @return distance parameter for the reference stock pivot
	 */
	private double getBestFibDistance(StockPivot baseStockPivot, StockPivot nullPivot) {

		// calculate the distance between min and max values
		double distanceMinMaxY = nullPivot.getValue() - baseStockPivot.getValue();
		double distanceMinMaxX = dbUtil.recordsInBetween(symbol, nullPivot.getLocation(), baseStockPivot.getLocation());
		double tgMinMax = Math.abs(distanceMinMaxY / distanceMinMaxX);

		// tangens for the last candle
		AxHistory lastSaved = dbUtil.getLastHistory(new Date(), symbol);
		double distanceLastY = lastSaved.getClose() - baseStockPivot.getValue();
		double distanceLastX = dbUtil.recordsInBetween(symbol, lastSaved.getDate(), baseStockPivot.getLocation());
		double tgLast = Math.abs(distanceLastY / distanceLastX);

		double tgRef;
		// iterate wile the tg is out of the range
		do {
			// reverence tangens for fib level 62.8
			tgRef = tgMinMax * 0.628;

			tgMinMax = tgMinMax / 2;
		} while (tgRef > tgLast);

		return tgMinMax * 2;
	}

	/**
	 * @return calculated next possible Stock Pivot value based on fib FAN level
	 */
	/*
	 * (non-Javadoc)
	 * 
	 * @see hu.myai.calculators.AbstractCalculator#getNexts()
	 */
	@Override
	public KeyValue[] getNexts() {

		StockPivot lastPivot = stockPivotList.get(stockPivotList.size() - 1);

		// X value between ref pivot and the last pivot
		double refX = dbUtil.recordsInBetween(symbol, referenceStock.getLocation(), lastPivot.getLocation());
		// Y value between ref pivot and the last pivot
		double refY = lastPivot.getValue() - referenceStock.getValue();

		AxHistory lastSaved = dbUtil.getLastHistory(new Date(), symbol);
		// X value between ref pivot and the candle after last
		double predX = dbUtil.recordsInBetween(symbol, referenceStock.getLocation(), lastSaved.getDate()) + 1;

		// this is the 100% level at predX position
		double fullY = predX * refY / (refX * getPercentFromFanSupport(lastPivot));

		// levels next to last pivot
		KeyValue[] valuePairsFibFan = { null, null };
		boolean found = true;

		// List<KeyValue> retValues = new ArrayList<KeyValue>();

		// double nextValue = 0;

		for (AbstractNode lastAbstractNode : getLastFibFanNode().getNextNodeList()) {

			// last pivot has fibo level
			FibNode lastFibFanNode = (FibNode) lastAbstractNode;

			// calculate the level for the current fibo at prexX position
			double levelValue = referenceStock.getValue() + (fullY * lastFibFanNode.getFibLevel());

			if (levelValue > 0) {
				// retValues.add(new KeyValue(false, lastFibFanNode.getName(),
				// StockPivot.valueNormalizer(levelValue), lastFibFanNode));

				// get the closest fib fan levels
				if (levelValue < lastPivot.getValue() && (found || valuePairsFibFan[1] == null)) {
					valuePairsFibFan[0] = new KeyValue(false, lastFibFanNode.getName(), StockPivot
							.valueNormalizer(levelValue), lastFibFanNode);
					found = false;
				} else if (levelValue > lastPivot.getValue() && (found || valuePairsFibFan[0] == null)) {
					valuePairsFibFan[1] = new KeyValue(false, lastFibFanNode.getName(), StockPivot
							.valueNormalizer(levelValue), lastFibFanNode);
					found = false;
				}
			}
		}
		return valuePairsFibFan; // StockPivot.valueNormalizer(nextValue);
	}

	/**
	 * @return the last stock pivots that has a Fibonacci Fan level assigned
	 */
	protected FibNode getLastFibFanNode() {
		for (int i = stockPivotList.size(); i > 0; i--) {
			if (getFibFanNode(stockPivotList.get(i - 1)) != null)
				return getFibFanNode(stockPivotList.get(i - 1));
		}
		return new FibNode();
	}

}
